package org.marsdonne.commons;

import com.github.oxo42.stateless4j.StateMachine;
import com.github.oxo42.stateless4j.StateMachineConfig;

import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.PostLoad;
import javax.persistence.Transient;

/**
 * 通用审核实体基类
 */
@MappedSuperclass
public abstract class ExaminableEntity extends VersionedIdentifiableEntity {

    @Column(nullable = false)
    protected ExaminationState state;

    public void setState(ExaminationState state) {
        this.state = state;
        this.initStateMachine();
    }

    public ExaminationState getState() {
        return state;
    }

    @Transient
    private final StateMachineConfig<ExaminationState, ExaminationTrigger> STATE_MACHINE_CONFIG = new StateMachineConfig<>();
    @Transient
    private StateMachine<ExaminationState, ExaminationTrigger> stateMachine;

    protected void handleApproval() {
    }

    protected void handleRejection() {
    }

    protected void handleCancellation() {
    }

    public ExaminableEntity() {
        this.state = ExaminationState.Applied;

        STATE_MACHINE_CONFIG.configure(ExaminationState.Applied)
                .permit(ExaminationTrigger.Approve, ExaminationState.Approved)
                .permit(ExaminationTrigger.Reject, ExaminationState.Rejected)
                .permit(ExaminationTrigger.Cancel, ExaminationState.Cancelled);

        STATE_MACHINE_CONFIG.configure(ExaminationState.Approved)
                .onEntry(this::handleApproval)
                .permit(ExaminationTrigger.Cancel, ExaminationState.Cancelled);

        STATE_MACHINE_CONFIG.configure(ExaminationState.Rejected)
                .onEntry(this::handleRejection)
                .permit(ExaminationTrigger.Apply, ExaminationState.Applied);

        STATE_MACHINE_CONFIG.configure(ExaminationState.Cancelled)
                .onEntry(this::handleCancellation)
                .permit(ExaminationTrigger.Apply, ExaminationState.Applied);

        this.initStateMachine();
    }

    @PostLoad
    public void initStateMachine() {
        this.stateMachine = new StateMachine<>(this.state, this.STATE_MACHINE_CONFIG);
    }

    public boolean process(ExaminationTrigger trigger) {
        boolean result = this.stateMachine.canFire(trigger);
        if (result) {
            this.stateMachine.fire(trigger);
            this.state = this.stateMachine.getState();
        }
        return result;
    }
}
